package com.luo.service.content.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.luo.dao.ArticleDao;
import com.luo.pojo.*;
import com.luo.service.content.ContentService;
import com.luo.utils.RedisUtil;
import io.redisearch.Document;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.WildcardQueryBuilder;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.luo.pojo.Date;

/**
 * @author Luo
 */
@Service("ContentService")
public class ContentServiceImpl implements ContentService {
    @Autowired
    private ArticleDao articleDao;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RestHighLevelClient client;

    @Override
    public void addArticle(Article article) {
    }

    @Override
    public void deleteArticleById(Integer aid) {

    }

    @Override
    public void updateArticleById(Article article) {
        this.articleDao.updateArticle(article);
    }

    @Override
    public void updateCategory(String ordinal, String newCatefory) {

    }

    @Override
    public void updateContentByCid(Article article) {

    }

    @Override
    public Article getArticleById(Integer aid) {
        return this.articleDao.selectArticle(aid);
    }


    @Override
    //@Log(logStr = "获取所有文章信息")
    public List<Article> getAllArticle() {
        return this.articleDao.queryArticle();
    }

    @Override
    public String[] getArticleTags(Integer aid) {
        return this.articleDao.queryArticleTags(aid);
    }

    @Override
    public List<String> queryTag() {
        return this.articleDao.queryTags();
    }

    @Override
    public List<Article> queryTagArticles(String tag) {
        return this.articleDao.queryTagArticles(tag);
    }

    @Override
    public String[] queryDate() {
        List<Date> dates = this.articleDao.queryDate();
        // String[] dateArray = new String[articles.size()];
        String[] dateArray = new String[dates.size()];
        int count = 0;
        for (Date date : dates) {
            if (Integer.parseInt("10") > Integer.parseInt(date.getMonth())) {
                String searchWord = date.getYear() + "-0" + date.getMonth();
                dateArray[count] = searchWord;
            } else {
                String searchWord = date.getYear() + "-" + date.getMonth();
                dateArray[count] = searchWord;
            }
            count++;
        }
        return dateArray;
    }

    @Override
    public String[] queryCategories() {
        return this.articleDao.queryCategories();
    }

    @Override
    public String getCategory(int aid) {
        return this.articleDao.getArticleCat(aid);
    }

    @Override
    public List<Article> queryCateArticles(String category) {
        return this.articleDao.queryCateArticles(category);
    }

    @Override
    public List<Tag> selectTagsById() {
        return this.articleDao.selectTagsById();
    }

    @Override
    public int deleteTag(Integer tid) {
        return this.articleDao.deleteTag(tid);
    }

    @Override
    public int updateTag(Integer tid, String tag) {
        return this.articleDao.updateTag(tid,tag);
    }

    @Override
    public List<Tag> getTagsByTag(String tag) {
        return this.articleDao.getTagsByTag(tag);
    }

    @Override
    public int insertTag(String tag) {
        return this.articleDao.insertTag(tag);
    }

    @Override
    public int deleteCate(Integer cid) {
        return this.articleDao.deleteCate(cid);
    }

    @Override
    public List<Category> selectCateById() {
        return this.articleDao.selectCateById();
    }

    @Override
    public int updateCate(Integer cid, String cate) {
        return this.articleDao.updateCate(cid,cate);
    }

    @Override
    public List<Category> getCateByCate(String cate) {
        return this.articleDao.getCateByCate(cate);
    }

    @Override
    public int insertCate(String cate) {
        return this.articleDao.insertCate(cate);
    }

    @Override
    public int insertArticle(Integer cid, String title, Integer isPublish, Integer readTime, String discribe, Integer recommed,String content) {
        return this.articleDao.insertArticle(cid,title,isPublish,readTime,discribe,recommed,content);
    }

    @Override
    public int bindArticleAndTag(List<ArticleTag> articleTagList) {
        return this.articleDao.bindArticleAndTag(articleTagList);
    }

    @Override
    public Article getArticleByTitle(String title) {
        return this.articleDao.getArticleByTitle(title);
    }

    @Override
    public String saveViews(String route) {
        //从redis中获取浏览量
//        System.out.println(route);
        String views = (String)redisUtil.get(route);
        String nowTime = (String) redisUtil.get(route+"now_time");
        redisUtil.set(route+"now_time",new java.util.Date().getTime()/1000);
        int inteval = 0;
        //控制时间间隔
        if(nowTime==null){
            inteval = Integer.parseInt((String)redisUtil.get(route+"now_time"));
        }else{
            inteval = Integer.parseInt((String)redisUtil.get(route+"now_time")) - Integer.parseInt(nowTime);
        }
        //控制浏览量自增
        if(views==null){
            redisUtil.set(route,"1");
        }else{
            if(route.equals("Visit_users")){
                //1小时控制访问人数变化
                if(inteval>3600){
                    redisUtil.incr(route,1);
                }
            }else if(route.equals("Visit_views")){ //1分钟控制网站浏览量
                if(inteval>60){
                    redisUtil.incr(route,1);
                }
            }else{
                if(inteval>30){
                    redisUtil.incr(route,1);
                }
            }
        }
        return (String)redisUtil.get(route);
    }

    @Override
    public int addArticleViews(int aid, int add_count) {
        return this.articleDao.addArticleViews(aid,add_count);
    }


    @Override
    public Boolean putArticleIntoES() {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("2m");
        for (Article article : this.articleDao.queryArticle()) {
            if (article.getIsPublish() == 1) {
                article.setTags(this.articleDao.queryArticleTags(article.getAid()));
                bulkRequest.add(new IndexRequest(ContentService.ES_DOCUMENT)
                        .id(""+article.getAid())
                        .source(JSON.toJSONString(article),XContentType.JSON)
                );
            }
        }
        try {
            BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            return !bulk.hasFailures();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public List<Map<String,Object>> searchArticleByES(String keyword) {
        SearchRequest searchRequest = new SearchRequest(ContentService.ES_DOCUMENT);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

       BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
       if(keyword!=null){
           boolQuery.should(QueryBuilders.matchQuery("title",keyword))
                    .should(QueryBuilders.termQuery("tags",keyword))
                    .should(QueryBuilders.matchQuery("discribe",keyword));
       }
       searchSourceBuilder.query(boolQuery);

        //高亮
        HighlightBuilder highlighter = new HighlightBuilder();
        highlighter.field("title").field("tags");
        highlighter.preTags("<strong style='color:red'>");
        highlighter.postTags("</strong>");
        searchSourceBuilder.highlighter(highlighter);

        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
            //解析结果
            ArrayList<Map<String,Object>> list = new ArrayList<>();
            for (SearchHit hit : search.getHits().getHits()) {
                //解析高亮的字段
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                HighlightField title =highlightFields.get("title");
                HighlightField tags = highlightFields.get("tags");
                Map<String,Object> sourceAsMap = hit.getSourceAsMap();//原来字段的结果
                if(title!=null){
                    Text[] fragments = title.fragments();
                    String newTitle = "";
                    for (Text fragment : fragments) {
                        newTitle+=fragment;
                    }
                    sourceAsMap.put("title",newTitle); //高亮的字段替换成原来的结果
                }
                if(tags!=null){
                    Text[] fragments = tags.fragments();
                    String newTag = "";
                    for (Text fragment : fragments) {
                        newTag+=fragment;
                    }
                    sourceAsMap.put("tags",newTag); //高亮的字段替换成原来的结果
                }
                list.add(hit.getSourceAsMap());
            }
            return list;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public RestStatus updateArticleES(Article article) {
        UpdateRequest updateRequest = new UpdateRequest(ContentService.ES_DOCUMENT, article.getAid() + "");
        updateRequest.timeout("1s");
        updateRequest.doc(JSON.toJSONString(article), XContentType.JSON);
        try {
            return client.update(updateRequest,RequestOptions.DEFAULT).status();
        } catch (IOException e) {
            e.printStackTrace();
            return RestStatus.INTERNAL_SERVER_ERROR;
        }
    }

    @Override
    public RestStatus insertArticleES(Article article) {
        article.setTags(this.articleDao.queryArticleTags(article.getAid()));
        IndexRequest indexRequest = new IndexRequest(ContentService.ES_DOCUMENT);
        indexRequest.id(""+article.getAid()).timeout("1s");
        indexRequest.source(JSON.toJSONString(article),XContentType.JSON);
        try {
            return client.index(indexRequest,RequestOptions.DEFAULT).status();
        } catch (IOException e) {
            e.printStackTrace();
            return RestStatus.INTERNAL_SERVER_ERROR;
        }
    }

    @Override
    public RestStatus deleteArticleES(Integer aid) {
        DeleteRequest deleteRequest = new DeleteRequest(ContentService.ES_DOCUMENT, aid + "");
        deleteRequest.timeout("1s");

        try {
            return client.delete(deleteRequest,RequestOptions.DEFAULT).status();
        } catch (IOException e) {
            e.printStackTrace();
            return RestStatus.INTERNAL_SERVER_ERROR;
        }
    }

    @Override
    public Boolean insertArticleRS(Article article) {
        JSONObject jsonObject = JSONUtil.parseObj(article);
        boolean index = redisUtil.createIndex(jsonObject);
        if (index){
            return redisUtil.addDocument(new Document(article.getAid().toString()), JSONUtil.toBean(jsonObject, Map.class));
        }
        return false;
    }

    @Override
    public void delAboutArticlesCache() {
        Set<String> categories = redisUtil.keys(RedisCacheEnum.Categories.getKey() + "*");
        Set<String> tags = redisUtil.keys(RedisCacheEnum.Tags.getKey() + "*");
        redisUtil.del(RedisCacheEnum.Post.getKey(),
                RedisCacheEnum.Date_Articles.getKey(),
                RedisCacheEnum.Recommend_Article.getKey());
        categories.forEach(key1->redisUtil.del(key1));
        tags.forEach(key2->redisUtil.del(key2));
    }
}
